<?php
/* SVN FILE: $Id: scripture.php 71 2007-12-10 00:21:14Z joe.vasquez $ */
/**
 * scripture model
 * @filesource
 * @author			Joe Vasquez <joe.vasquez@gmail.com>
 * @copyright		Copyright (c) 2007, Joe Vasquez
 * @package			redlettr
 * @subpackage		scriptures
 * @since			0.1A
 * @version			0.1A  $Rev: 71 $
 * @lastmodified	$Date: 2007-12-10 00:21:14 +0000 (Mon, 10 Dec 2007) $
 * @license			http://www.gnu.org/licenses/gpl.html GNU General Public License
 */
 
class Scripture extends AppModel {

	var $name = 'Scripture';
	var $__key = 'TEST';
	var $__get = 'http://www.gnpcb.org/esv/share/get?';
	var $__newTags = array();
	
	var $hasAndBelongsToMany = array(
		'Tag'	=> array(
			'className'				=> 'Tag',
			'join_table'			=> 'scriptures_tags',
			'foreignKey'			=> 'scripture_id',
			'associationForeignKey'	=> 'tag_id',
			'unique'				=> true
		)	
	);
	
	public $options = array(
			'q'					=> '',
			'not-words'			=> '',
			'matches'			=> '',
			'scope'				=> 'whole bible',
			'searchtext'		=> 'all',		
			'page'				=> '1',
			'results-per-page'	=> '25',
			'link-url'			=> ''	
	);
	
	public $overwriteOptions = array(
		'link-url'	=> ''
	);

/**
 * @var array list of regex used to normalize search string
 * @access private
 * @see __fixString()
 */	
	private $__findNReplace = array(
		'/[ ]{2,}/'		=> ' ',
		'/["]/'			=> '\'',
		'/[\']{2,}/'	=> '\'',
		'/\s\'/'		=> '\'',
		'/\'\s/'		=> '\'',
		'/[\-]{2,}/'	=> '-',
	);
	
/**
 * @access private
 * @var array list of common words to remove from search string
 * @see $__findNReplace
 */
	private $__commonWords = array(
		'the',
		'that',
		'this',
		'two',
		'and',
		'was',
		'for',
		'with',
		'are',
		'were',
	);
	
/**
 * @var integer minimum length for search string
 * @access private
 * @see $__findNReplace
 */	
	private $__minLength = 3;

/**
 * doQuery
 * 
 * takes 'q' from $this->data
 * 
 * @access public
 * @return mixed false on failure, results set on success
 * @link http://www.gnpcb.org/esv/share/services/api/#doQuery ESV API doQuery
 */	
	function doQuery()
	{
		if(!array_key_exists('q',$this->data))
		{
			return false;
		}

		$tmp = array();
		$strings = $this->__fixString($this->data['q'],true);
		$this->data['q'] = r_implode($strings,' ');
		
		$tmp[] = 'key='.$this->__key;
		$tmp[] = 'action=doQuery';
		
		foreach($this->data as $key => $value)
		{
			$tmp[] = $key.'='.$value;
		}
		sort($tmp);
		$results = '';
		
		if(($conn = fopen($this->__get.implode('&',$tmp),'r')) !== false)
		{
			
			$results = stream_get_contents($conn);
			fclose($conn);
			$this->__fixResults($results);
			$this->__addSearchResults($results,am(implode(' ',$strings[0]),$strings[1]));
			$results = urldecode($results);
			return $results;
		}
		return false;
	}
	
	function __addSearchResults($results,$tags)
	{
		$links = $this->__extractPassageLinks($results);
		$tagIds = $this->Tag->getTagIds($tags,true);
		foreach($links as $link)
		{
			$link = strtolower($link);
			$parts = array();
			$hash = Security::hash($link);
			if(
				preg_match_all('/([0-9]*[a-z]+)\s([0-9]+)+:([0-9]+)/i',$link,$parts) !== false
				&&
				count($parts) == 4
				&&
				$this->findCount('Scripture.hash=\''.$hash.'\'') == 0
			)
			{
				$this->create(
					array(
						'Scripture' => array(
							'hash'			=> $hash,
							'book'			=> $parts[1][0],
							'start_chapter'	=> $parts[2][0],
							'end_chapter'	=> $parts[2][0],
							'start_verse'	=> $parts[3][0],
							'end_verse'	=> $parts[3][0],
							'count'			=> 1
						),		
						'Tag'				=> array(
							'Tag' 			=> array_keys($tagIds)
						)
					)
				);
				$this->save();
			}
		}
	}	

/**
 * __fixString
 * 
 * normalizes string for word search
 * 
 * @access private
 * @param string string search string to "fix"
 * @return array sorted arrays of words and phrases
 */
	function __fixString($string)
	{
		$string = low(trim($string));
		$string = preg_replace(
			array_keys($this->__findNReplace),
			array_values($this->__findNReplace),
			$string
		);
		$lastQuotePos = null;
		$lastWordPos = null;
		$quoteLength = 0;
		$wordLength = 0;
		$words = array();
		$phrases = array();
		for($i = 0; $i < strlen($string); $i++)
		{
			if(strcmp($string[$i],'\'') == 0)
			{
				if(is_null($lastQuotePos))
				{
					$lastQuotePos = $i;
					$words[] = substr($string,$lastWordPos,$wordLength);
					$lastWordPos = null;
					$wordLength = 0;
				} else {
					$phrases[] = substr($string,$lastQuotePos,2+$quoteLength);
					$lastQuotePos = null;
					$quoteLength = 0;
				}	
			} else {
				if(is_null($lastQuotePos))
				{
					if(is_null($lastWordPos))
					{
						$lastWordPos = $i;
					} else {
						if(preg_match('/\s/',$string[$i]) == 1)
						{
							$words[] = substr($string,$lastWordPos,++$wordLength);
							$lastWordPos = null;
							$wordLength = 0;								
						} else {
							$wordLength++;
						}
					}
				} else {
					$quoteLength++;
				}
			}	
		}
		//if unmatched quote exists, add string after that quote to the words array
		if(!is_null($lastQuotePos))
		{
			$words[] = substr($string,$lastQuotePos);
		}
		if(!is_null($lastWordPos))
		{
			$words[] = substr($string,$lastWordPos);
		}
		foreach($words as $id => $val)
		{
			if(strlen($val) < $this->__minLength || in_array($val,$this->__commonWords))
			{
				unset($words[$id]);
			} else {
				$words[$id] = trim($words[$id]);
			}
		}
		sort($words);
		sort($phrases);
		return array($words,$phrases);
	}

/**
 * __extractPassageLinks
 * 
 * @access private
 * @var string string
 * @return array all passages returned in search result
 */	
	function __extractPassageLinks($string)
	{
		$links = array();
		if(preg_match_all('/<a[^>]+>([a-z0-9\s]+:[0-9]+)<\/a>/i',$string,$links) !== false)
		{
			if(count($links) == 2)
			{
				return $links[1];
			} 
		}
		return array();
	}
	
/**
 * fix links
 * 
 * needed to remove "?" from links
 * 
 * @param string restults search results
 */	
	function __fixResults(&$results)
	{	
		$results = preg_replace(
			array(
				'/href="\?/i',
				'/&amp;/i'
			),
			array(
				'href="/scriptures/search/',
				'/'
			),
			$results
		);
	}
}
?>